/*************************
Author: saki
Data: 20171031
**************************/


#include "include/alexa.h"
#include <queue>
#include <vector>
#include <fstream>
#include <string>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <typeinfo>
#include <thread>
#include <sys/ioctl.h>


using namespace std;
typedef short SAMPLE;

ALEXA_DEVICE devices;
string Workdir;
//音频缓存队列
vector<char> block;
queue<vector<char>> blocks;

const int MaxBlock = 1000;

struct WriteThis {
  const char *readptr;
  long sizeleft;
};

FILE* f;
PaError err = paNoError;
PaStream *stream;
int Alexa::writeAudio = 1;
int uploadCount = 0;
int rec_status = 0;






Alexa::Alexa()
{
//    init();
}

Alexa::~Alexa()
{
    Pa_CloseStream(stream);
    Pa_Terminate();
}


size_t Upload(void *ptr, ALEXA_DEVICE devices)
{

    if(!blocks.empty()){
        cout<<"Queue Size is : "<<blocks.size()<<endl;
        vector<char> tmp;

        tmp = blocks.front();//取队列最前端数据
        memcpy(ptr,&tmp[0],devices.recTruckSize*2);

        blocks.pop();//剔除最前端数据
        fwrite(ptr,1,devices.recTruckSize*2,f);//音频数据写入文件

        uploadCount++;
        if(uploadCount==20){
            cout<<"############### UPLOADING ###############\n\r"<<endl;
            uploadCount=0;
        }
        return devices.recTruckSize*2;//上传数据
    }
    else{
        memset(ptr,0,2);
        fwrite(ptr,1,2,f);
        return 2;
    }
}

//音频分块上传回调函数
size_t Alexa::trunkUpload(void *ptr) {

//    ALEXA_DEVICE device = ReadDeviceinfo(getWorkdir());
//    等待一小段时间，同步采样和上传速度
//    usleep(35000);
    switch(rec_status)
    {
    case 0:
        cout<<"\n\n############### UPLOAD FINISHED ################\n\r"<<endl;
        for(int i=0;i<blocks.size();i++){
            blocks.pop();//上传结束，清空队列
            uploadCount=0;
        }
        return 0;
    case 1:
         return Upload(ptr,devices);
    }

    cout<<"\n\n############## UPLOAD RETURNED IRREGULAR ###############\n\r"<<endl;
    return 0;
}


//回调函数，接收服务器发送回来的所有数据并写入stream指向的文件中
size_t Alexa::write_data(void *ptr, size_t size, size_t nmemb, void *stream)
{
    cout<<"writing data..."<<endl;
    int written = fwrite(ptr,size,nmemb,(FILE *)stream);
    fflush((FILE *)stream);
    return written;
}


//回调函数
size_t Alexa::ReWriteConfig(void *ptr)
{
    cout<<"writing config data..."<<endl;
    Document doc,_doc;
    doc.SetObject();
    Document::AllocatorType &allocator = doc.GetAllocator();
    size_t t= strlen((char*)ptr);
    doc.Parse<0>((char*)ptr);

    ifstream fin((Workdir+"/config.json").c_str());
    stringstream buffer;
    buffer<<fin.rdbuf()<<endl;
    string contents(buffer.str());
    _doc.Parse<0>(contents.c_str());

    Value &client_id = _doc["client_id"];
    Value &client_secret = _doc["client_secret"];
    Value &s_audio_path = _doc["s_audio_path"];
    Value &r_audio_path = _doc["r_audio_path"];
    Value &sys_audio_path = _doc["sys_audio_path"];
    fin.close();


    doc.AddMember("client_id",client_id,allocator);
    doc.AddMember("client_secret",client_secret,allocator);
    doc.AddMember("s_audio_path",s_audio_path,allocator);
    doc.AddMember("r_audio_path",r_audio_path,allocator);
    doc.AddMember("sys_audio_path",sys_audio_path,allocator);
    StringBuffer buf;
    PrettyWriter<StringBuffer> pretty_writer(buf);
    doc.Accept(pretty_writer);

    ofstream fout((Workdir+"/config.json").c_str());
    fout<<buf.GetString()<<endl;
    fout.close();
    return t;
}


//检测键盘输入
void Alexa::KeyboardInput(char* cmd)
{
   
   thread p1(&Alexa::_KeyboardInput,this,cmd);//创建新的线程检测输入，避免主线程阻塞
   p1.detach();
}

void Alexa::_KeyboardInput(char* cmd)
{
    while(1){
        cin>>cmd;
    }

}

//回调函数，接收Downchannel链接收到的数据并解析，目前仅能解析StopCapture信息，其它待增加
size_t Alexa::DownChannelParser(void *ptr, size_t size, size_t nmemb, void *pointer)
{
    cout<<"---------------W-R-I-T-I-N-G-----------------"<<endl;
    char *p1 = strstr((char*)ptr,"Content-Type: application/json");
    p1 = p1 + 32;
    char *p2 = strstr(p1,"--");
    memset(p2,'\0',1);
//    cout<<p1<<endl;
    DownParams * params = (DownParams*)pointer;
    IsEndpoint(p1, params);
    if(params->mark == ENDPOINT_DIRE){
        rec_status = 0;
        err = Pa_StopStream(stream);//停止音频录制流
        fclose(f);
    }
    else if(params->mark == SETALERT_DIRE){

    }
    return size*nmemb;
}

//Alexa Skill interface
void Alexa::Skill(string directiveJson)
{
    static string start_token = "b4210b93-a504-492a-8998-363cd2edae80";
    static string stop_token  = "50c74b98-dcb0-417c-91cd-12a8647207a7";

    size_t index = directiveJson.find("cid:ValidatedSpeakDirective_amzn1.ask.skill.");

    if(-1 != index)
    {
        string tmpJson = directiveJson.substr(index+44,36);
        cout<<tmpJson<<endl;

        if(start_token == tmpJson)
        {
            /******start code edit here******/
            cout<<"music play"<<endl;


            /********************************/
        }
        else if(stop_token == tmpJson)     
        {
            /******stop code edit here******/
            cout<<"music stop"<<endl;


            /*******************************/
        } 
        else
        {
            ;
        }

    }

}

//解析从Events Channel接收的multipart数据，multipart数据包含header、json、audio三个部分
//每个部分由boundary字符串分割
string Alexa::Parser(string &json)
{
//    string Time = getTime();
    ifstream fin((Workdir + "/.cache").c_str(),ios::binary);
    string temp;
    size_t b_index, e_index;
    bool _start1 = false;
    bool _start2 = false;

    while(getline(fin,temp,'\n'))//首先在header中寻找作为boundary的字符串
    {
        b_index = temp.find("boundary");
        if(b_index!=-1)
        {
            e_index = temp.find(";start");
            break;
        }

    }

    string boundary ="--"+temp.substr(b_index+9,e_index-b_index-9);
    cout<<boundary<<endl;

    //根据boundary截取json部分
    while(getline(fin,temp,'\n'))
    {
        b_index = temp.find(boundary);

        if(_start1)
        {
            json = temp;
            _start1 = false;
            break;
        }

        if(b_index!=-1){
            _start1 = true;
            getline(fin,temp,'\n');
            getline(fin,temp,'\n');
        }
    }
    //    cout<<json<<endl;
    Skill(json);

    string path = config.r_audio_path + nowtime + ".mp3";
    ofstream fout(path.c_str(), ios::binary);
    
    //根据boundary截取音频内容
    while(getline(fin,temp,'\n'))
    {
        b_index = temp.find(boundary);

        if(b_index!=-1){

            for(int i=0;i<3;i++){
                getline(fin,temp,'\n');
                if(temp.find("Content-Type: application/octet-stream")!=-1){
                    _start2 = true;
                    break;
                }
            }
        }

        if(_start2==true){
            fout<<temp<<'\n';
        }
    }

    fout.close();
    fin.close();

    return path;
}


//HTTP POST刷新access_token
//已完成：每次运行程序先刷新一次
//待完成：检测access_token是否过期，即将过期时再次刷新
void Alexa::FlashToken(CURL *curl, struct curl_slist *head)
{
//    ALEXA_CONFIG allconfig = Readtoken(getWorkdir());

    static string data = "grant_type=refresh_token&refresh_token=" \
                         + config.refresh_token \
                         + "&client_id="+config.client_id \
                         + "&client_secret="+config.client_sceret;//需要POST的字符串

//    FILE *_config;
    head = curl_slist_append(head,"Content-Type: application/x-www-form-urlencoded");

    curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
    curl_easy_setopt(curl,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_2_0);
    curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1L);
    curl_easy_setopt(curl,CURLOPT_FORBID_REUSE,1L);
    curl_easy_setopt(curl,CURLOPT_HTTPHEADER,head);
    curl_easy_setopt(curl,CURLOPT_URL,"https://api.amazon.com/auth/o2/token");
    curl_easy_setopt(curl,CURLOPT_POSTFIELDS,data.c_str());
    curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,ReWriteConfig);//回调函数接收新的token并写入config
//    curl_easy_setopt(curl,CURLOPT_WRITEDATA,_config);
}

//HTTP GET 建立DownChannel链接并保持，接收例如StopCapture等数据信息
void Alexa::SetDownChannel(CURL *curl, struct curl_slist *header, struct DownParams * params)
{
    string auth = "Authorization:Bearer " + config.access_token;

    curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
    curl_easy_setopt(curl,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_2_0);
    curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1L);
    curl_easy_setopt(curl,CURLOPT_FORBID_REUSE,1);
    curl_easy_setopt(curl,CURLOPT_TIMEOUT,3600L);

    curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,DownChannelParser);//回调函数，解析接收到的数据
    curl_easy_setopt(curl,CURLOPT_WRITEDATA, params);
    header = curl_slist_append(header,auth.c_str());

    curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
    curl_easy_setopt(curl,CURLOPT_HTTPHEADER,header);
    curl_easy_setopt(curl,CURLOPT_URL,"https://avs-alexa-na.amazon.com/v20160207/directives");
}


//每隔5min通过HTTP GET的方式ping服务器，避免DownChannel链接断开
//(待添加5min定时功能)
void Alexa::Ping(CURL *curl, struct curl_slist *header)
{
    string auth = "Authorization:Bearer " + config.access_token;

    curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
    curl_easy_setopt(curl,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_2_0);
    curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1L);
    curl_easy_setopt(curl,CURLOPT_FORBID_REUSE,1);

    header = curl_slist_append(header,auth.c_str());

    curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
    curl_easy_setopt(curl,CURLOPT_HTTPHEADER,header);
    curl_easy_setopt(curl,CURLOPT_URL,"https://avs-alexa-na.amazon.com/ping");

}

//HTTP POST向服务器同步本地状态信息，每次程序运行先要进行同步，状态信息由json格式构建
void Alexa::Synchronize(CURL *curl,struct curl_slist *header,struct curl_httppost *postFirst,struct curl_httppost *postLast)
{

    string auth = "Authorization:Bearer " + config.access_token;

    string json;
//    Event eventState = {"System",
//                        "SynchronizeState",
//                        "TAP"};

    struct Event eventState;
    eventState.header.namesp = "System";
    eventState.header.name = "SynchronizeState";
    eventState.header.msgid = "message-123";

    MultiMsgPt1(json,eventState);//json

    cout<<json<<endl;
    header = curl_slist_append(header,auth.c_str());
    header = curl_slist_append(header,"content-type:multipart/form-data");

    curl_formadd(&postFirst,&postLast,
                 CURLFORM_COPYNAME,"metadata",
                 CURLFORM_COPYCONTENTS,json.c_str(),
                 CURLFORM_CONTENTTYPE,"application/json; charset=UTF-8",
                 CURLFORM_END);//构建multipart数据

    curl_easy_setopt(curl,CURLOPT_FORBID_REUSE,1L);
    curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
    curl_easy_setopt(curl,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_2_0);
    curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1L);

    curl_easy_setopt(curl,CURLOPT_HTTPHEADER,header);
    curl_easy_setopt(curl,CURLOPT_URL,"https://avs-alexa-na.amazon.com/v20160207/events");
    curl_easy_setopt(curl,CURLOPT_HTTPPOST,postFirst);
}

//HTTP POST建立EventChannel，发送包含音频的multipart数据
void Alexa::SendAudioFile(CURL *curl,struct curl_slist *header,curl_httppost *postFirst, curl_httppost *postLast)
{
    string auth = "Authorization:Bearer " +config.access_token;
    rec_status = 1;
    FILE *saveHeaderFile;

    saveHeaderFile = fopen((Workdir+"/.cache").c_str(),"w+");

    char *buffer = (char*)malloc(64);
    static struct WriteThis pooh;
    pooh.readptr = (const char*)buffer;
    pooh.sizeleft = 64;

    string json;
    struct Event eventState;
    eventState.header.namesp = "SpeechRecognizer";
    eventState.header.name = "Recognize";
    eventState.header.msgid = "message-123";
    eventState.header.dialogRequestid = "dialogrequest-321";
    eventState.payload.initiator = "TAP";
    eventState.payload.profile = "NEAR_FIELD";
    eventState.payload.format = "AUDIO_L16_RATE_16000_CHANNELS_1";

    MultiMsgPt1(json,eventState);//状态信息

    header = curl_slist_append(header,auth.c_str());
    header = curl_slist_append(header,"content-type:multipart/form-data");

    curl_formadd(&postFirst,&postLast,
                 CURLFORM_COPYNAME,"metadata",
                 CURLFORM_COPYCONTENTS,json.c_str(),
                 CURLFORM_CONTENTTYPE,"application/json; charset=UTF-8",
                 CURLFORM_END);//json部分

    curl_formadd(&postFirst,&postLast,
                 CURLFORM_COPYNAME,"audio",
                 CURLFORM_STREAM,&pooh,
                 CURLFORM_CONTENTSLENGTH,0,
                 CURLFORM_CONTENTTYPE,"application/octet-stream",
                 CURLFORM_END);//音频部分

    curl_easy_setopt(curl,CURLOPT_TIMEOUT,100L);
    curl_easy_setopt(curl,CURLOPT_FORBID_REUSE,1L);
    curl_easy_setopt(curl,CURLOPT_READFUNCTION,trunkUpload);//通过回调函数上传音频数据
    curl_easy_setopt(curl,CURLOPT_HEADERFUNCTION,write_data);//接收返回的header数据
    curl_easy_setopt(curl,CURLOPT_HEADERDATA,saveHeaderFile);
    curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,write_data);//接收返回的multipart数据
    curl_easy_setopt(curl,CURLOPT_WRITEDATA,saveHeaderFile);
    curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
    curl_easy_setopt(curl,CURLOPT_HTTP_VERSION,CURL_HTTP_VERSION_2_0);
    curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,1L);
    curl_easy_setopt(curl,CURLOPT_HTTPHEADER,header);
    curl_easy_setopt(curl,CURLOPT_URL,"https://avs-alexa-na.amazon.com/v20160207/events");
    curl_easy_setopt(curl,CURLOPT_HTTPPOST,postFirst);
}






//音频采集函数
//void Alexa::capture(const char *filepath)
//{
//    //线程
//    thread t2(&Alexa::_capture,this,filepath);
//    t2.detach();
//}

void Alexa::capture()
{
    string filepath = config.s_audio_path + nowtime + ".pcm";
    cout<< filepath <<endl;

    f = fopen(filepath.c_str(),"wb");

    err = Pa_StartStream(stream);//开启录音流
    if(err != paNoError){
        cout<<"PortAudio start stream error: "<<Pa_GetErrorText(err)<<endl;
        exit(1);
    }
    cout<<"Now recording..."<<endl;

}

//停止音频采集函数
void Alexa::stopCapture()
{
    err = Pa_StopStream(stream);//停止录音流
    if(err != paNoError){
        cout<<"PortAudio close stream error: "<<Pa_GetErrorText(err)<<endl;
        exit(1);
    }
    
    rec_status = 0;
}

void Alexa::init()//功能初始化
{


    devices = device;
    Workdir = workdir;
    block.resize(sizeof(SAMPLE) * 512);
    err = Pa_Initialize();//Portaudio初始化
    if (err != paNoError){
        cout<<"PortAudio init error: "<<Pa_GetErrorText(err)<<endl;
        exit(1);
    }


    PaStreamParameters inputParameters;

    inputParameters.device=devices.useDeviceNum;//声卡设备号
    inputParameters.channelCount=1;//声道数
    inputParameters.sampleFormat=paInt16;//PCM格式
    inputParameters.suggestedLatency=Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
    inputParameters.hostApiSpecificStreamInfo=NULL;

    err = Pa_OpenStream(
                &stream,
                &inputParameters,
                NULL,
                16000,
                devices.recTruckSize,
                paClipOff,
                &CaptureCallback,//录音回调
                NULL);//开启流
    if(err != paNoError){
        cout<<"PortAudio open stream error: "<<Pa_GetErrorText(err)<<endl;
        exit(1);
    }

}

//录音回调函数，音频数据塞入音频缓存队列最后端
int CaptureCallback(const void *input,
                    void *output,unsigned long frameCount,
                    const PaStreamCallbackTimeInfo * timeInfo,
                    PaStreamCallbackFlags statusFlags,void *userData)
{

    memcpy(&block[0],input, sizeof(SAMPLE)*frameCount);

    if(blocks.size() > MaxBlock){
        blocks.pop();
    }
    blocks.push(block);//塞入队列
    return paContinue;
}
